<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <p>{{zhang.name}}---{{zhang.age}}</p>
        <p>{{zhang.hobby}}</p>
        <p>{{zhang.grade}}</p>
        <p>
            <button @click="changeAge">changeAge</button>
            <button @click="changeChinese">changeChinese</button>
        </p>
       
    </div>
</body>
<script type="module">
    // 整体载入  => 查看模块内暴露的变量和方法
    import * as Vue from "../js/vue.esm-browser.js";
    console.log(Vue);

    // 按需引入
    import { createApp, reactive,shallowReactive,getCurrentInstance } from "../js/vue.esm-browser.js";


    // 组合式写法 可以理解为是选项式语法的优化, 但是最终还是会被解析成选项式语法
    // 在 setup() 函数中返回的对象会暴露给模板和组件实例。其他的选项也可以通过组件实例来获取 setup() 暴露的属性
    let app = createApp({
        setup() {

            /* 
                reactive()  创建一个对 复杂类型(数组 对象) 的引用  => 返回的是Proxy代理对象(Vue处理版)

                    1.  reactive()   返回的是Proxy代理对象,可以直接使用数据
                    2.  reactive 对数据的检测也是深层的  => 如果proxy代理对象的某个属性的属性值是数组/对象类型, reactive()默认将其转化为Proxy代理

                    new Proxy(zhang,{
                        get(target,key){
                            track()  // 订阅操作  => 所有依赖当前数据的其他数据, 都是当前数据的订阅者
                            return target[key];
                        },
                        set(target,key,val){
                            target[key] = val;
                            trigger();  // 当数据被修改之后 更新视图,触发副作用操作(如果有其他数据依赖(订阅)当前数据,触发对应的副作用操作)
                        }
                    })

                缺点:
                    1. reactive() 一般接收复杂数据类型,不支持 string number boolean null undefined
                    2. 不能替换整个对象    => 所谓替换就是给变量赋新值, 原本引用的Proxy代理就被释放
                    3. 对解构操作不友好(丧失响应性)  当我们将响应式对象的原始类型属性解构为本地变量时，或者将该属性传递给函数时，我们将丢失响应性连接：
                        解构之后得到的是proxy代理对象中存储的值   


                shallowReactive()   reactive() 的浅层作用形式。
                    1. 返回的还是proxy代理
                    2. shallowReactive() 只会针对目标对象做Proxy代理,如果对象的某个属性的属性值是数组/对象,不会做任何操作  => 只关注对象代理对象根属性的变化
            
            */

            const obj = reactive({ x: 1, y: 2 });
            // const zhang = reactive({
            //     name: "张三",
            //     age: 18,
            //     hobby: ["唱", "跳", "rap"],
            //     grade: {
            //         chinese: 55,
            //         math: 66,
            //         english: 77,
            //     }
            // })

            const zhang = shallowReactive({
                name: "张三",
                age: 18,
                hobby: ["唱", "跳", "rap"],
                grade: {
                    chinese: 55,
                    math: 66,
                    english: 77,
                }
            })

            console.log(zhang);
            console.log(zhang.hobby);
            console.log(zhang.grade);

            /*  
                const cur = getCurrentInstance();  获取当前组件vue实例
                cur.appContext  根组件的vue实例
                cur.ctx  /cur.proxy     当前vue实例对应的proxy代理对象
                cur.attrs  当前vue实例(组件)被透传的属性   类似vue2 => this.$attrs  
                cur.props  当前vue实例(组件)运行接收的数据  类似vue2 => this.$props  
            
            */
            const cur = getCurrentInstance();
            console.log("cur",cur);



            const changeAge = () => {
                zhang.age++;
            }

            const changeChinese = () => {
                zhang.grade.chinese++;

                // vue2  => 获取当前组件的Proxy代理对象
                // this.$forceUpdate();

                // vue3 不能访问this  => 1. 找到当前vue实例的proxy代理  2. 访问当前vue实例的proxy代理中的$forceUpdate()
                cur.ctx.$forceUpdate();
            }

          



            return {
                zhang,
                changeAge,
                changeChinese,
            }

        },
        mounted() {
            console.log("mounted", this);

            /* 
                this => 当前vue实例的Proxy代理对象(模拟)
                Proxy => {   // 写法只是方便理解
                    get a(){
                        return a.value;
                    }
                    set a(val){
                        a.value = val;
                    }
                }

            
            
            */
        },
    })

    app.mount("#app");
</script>

</html>